/*
 * File      : rtthread.h
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006-2012, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rt-thread.org/license/LICENSE.
 *
 * Change Logs:
 * Date           Author       Notes
 * 2012-1-7       prife        the first version
*/
#include <rtthread.h>
#include <rtdevice.h>

#include "cyg/infra/cyg_type.h"
#include "cyg/fileio/fileio.h"
#include "port/codes.h"
#include "port/fcntl.h"
#undef mode_t

#include <dfs_fs.h>
#include <dfs_def.h>

#include "dfs_jffs2.h"
#include "jffs2_config.h"
#include "porting.h"
#include <string.h>

#if DEVICE_PART_MAX > 1
	#error "support only one jffs2 partition on a flash device!"
#endif
			
/* make sure the following struct var had been initilased to 0! */
struct device_part 
{
	struct cyg_mtab_entry * mte;
	struct rt_mtd_nor_device *dev;
};
static struct device_part device_partition[DEVICE_PART_MAX] = {0};

static struct rt_mutex jffs2_lock;

#define jffs2_mount   jffs2_fste.mount
#define jffs2_umount  jffs2_fste.umount
#define jffs2_open    jffs2_fste.open
#define jffs2_file_unlink  jffs2_fste.unlink
#define jffs2_mkdir   jffs2_fste.mkdir
#define jffs2_rmdir   jffs2_fste.rmdir
#define jffs2_rename  jffs2_fste.rename
#define jffs2_link    jffs2_fste.link
#define jffs2_opendir jffs2_fste.opendir
#define jffs2_chdir   jffs2_fste.chdir
#define jffs2_ops_stat    jffs2_fste.stat
#define jffs2_getinfo jffs2_fste.getinfo
#define jffs2_setinfo jffs2_fste.setinfo

#define jffs2_file_read    jffs2_fileops.fo_read
#define jffs2_file_write   jffs2_fileops.fo_write
#define jffs2_file_lseek   jffs2_fileops.fo_lseek
#define jffs2_file_ioctl   jffs2_fileops.fo_ioctl
#define jffs2_file_select  jffs2_fileops.fo_select
#define jffs2_file_fsync   jffs2_fileops.fo_fsync
#define jffs2_file_colse   jffs2_fileops.fo_close
#define jffs2_file_fstat   jffs2_fileops.fo_fstat
#define jffs2_file_getinfo jffs2_fileops.fo_getinfo
#define jffs2_file_setinfo jffs2_fileops.fo_setinfo

#define jffs2_dir_read    jffs2_dirops.fo_read
//#define jffs2_dir_write   jffs2_dirops.fo_write
#define jffs2_dir_lseek   jffs2_dirops.fo_lseek
//#define jffs2_dir_ioctl   jffs2_dirops.fo_ioctl
#define jffs2_dir_select  jffs2_dirops.fo_select
//#define jffs2_dir_fsync   jffs2_dirops.fo_fsync
#define jffs2_dir_colse   jffs2_dirops.fo_close
//#define jffs2_dir_fstat   jffs2_dirops.fo_fstat
//#define jffs2_dir_getinfo jffs2_dirops.fo_getinfo
//#define jffs2_dir_setinfo jffs2_dirops.fo_setinfo

/*
 * RT-Thread Device Interface for jffs2
 */

/* these code is in src/flashio.c */ 
static int jffs2_result_to_dfs(int result)
{
	int status = -1;

	if (result < 0)
		result = -result;

	switch (result)
	{
	case ENOERR:/** no error */
		break;
	case EACCES:/** Tried to open read-only file for writing, or files sharing mode
				   does not allow specified operations, or given path is directory */
		status = -DFS_STATUS_EINVAL;
		break;/* no suitable */
	case EEXIST:   /** _O_CREAT and _O_EXCL flags specified, but filename already exists */
		status = -DFS_STATUS_EEXIST;
		break;
	case EINVAL:  /** Invalid oflag or pmode argument */
		status = -DFS_STATUS_EINVAL;
		break;
	case EMFILE: /** No more file handles available(too many open files)  */
		rt_kprintf("dfs_jffs2.c error: no more file handles available\r\n");
		status = -1;
		break;//fixme
	case ENOENT: /** file or path not found */
		status = -DFS_STATUS_ENOENT;
		break;
	case EBADF: /** invalid file handle */
		status = -DFS_STATUS_EBADF;
		break;
	case ENOMEM:/** no enough memory */
		status = -DFS_STATUS_ENOMEM;
		break;
	case EIO: /** I/O error from lower level flash operation */
		status = -DFS_STATUS_EIO;
		break;
	case ENOTDIR: /** Not a directory */
		status = -DFS_STATUS_ENOTDIR;
		break;
	case EISDIR: /** Is a directory */
		status = -DFS_STATUS_EISDIR;
		break;
	case ENOSPC: /**//* No space left on device */
		status = -DFS_STATUS_ENOSPC;
		break;

	default:
		rt_kprintf("dfs_jffs2.c error: %d\r\n", result);
		status = -1;
		break; /* unknown error! */
	}

	return status;
}

/*
 * RT-Thread DFS Interface for jffs2
 */
static int dfs_jffs2_mount(struct dfs_filesystem* fs, 
                    unsigned long rwflag, 
				    const void* data)
{
	unsigned index;
	struct cyg_mtab_entry * mte;
	int result;

	/* find a empty entry in partition table */
	for (index = 0; index < DEVICE_PART_MAX; index ++)
	{
		if (device_partition[index].dev == RT_NULL)
			break;
	}
	if (index == DEVICE_PART_MAX) 
		return -DFS_STATUS_ENOSPC;

	mte = rt_malloc(sizeof(struct cyg_mtab_entry));
	if (mte == RT_NULL)
		return -DFS_STATUS_ENOMEM;

	mte->name = fs->path;
	mte->fsname = "jffs2";
	mte->devname = NULL;
	/* note that, i use mte->data to store rtt's device 
	 * while, in jffs2_mount, mte->data will be copy into
	 * s_dev in struct super_block, and mte->data will be
	 * filled with jffs2_sb(see the source of jffs2_mount.
	 */
	mte->data = (CYG_ADDRWORD)fs->dev_id;

	device_partition[index].dev = RT_MTD_NOR_DEVICE(fs->dev_id);
	/* after jffs2_mount, mte->data will not be dev_id any more */
	result = jffs2_mount(NULL, mte);
	if (result != 0)
	{	
		device_partition[index].dev = NULL;
		return jffs2_result_to_dfs(result);
	}
	/* save this pointer */
	device_partition[index].mte = mte;	
	return 0;
}

static int _find_fs(struct cyg_mtab_entry ** mte, rt_device_t dev_id)
{
	unsigned index;	
	/* find device index */
	for (index = 0; index < DEVICE_PART_MAX; index++)
	{
		if (device_partition[index].dev == RT_MTD_NOR_DEVICE(dev_id))
		{
			*mte = device_partition[index].mte;
			return 0;
		}
	}
	rt_kprintf("error, could not found the fs!");
	return -1;
}

static int dfs_jffs2_unmount(struct dfs_filesystem* fs)
{
	int result;
	unsigned index;
	/* find device index, then umount it */
	for (index = 0; index < DEVICE_PART_MAX; index++)
	{
		if (device_partition[index].dev == RT_MTD_NOR_DEVICE(fs->dev_id))
		{
			result = jffs2_umount(device_partition[index].mte);
			if (result)
				return jffs2_result_to_dfs(result);	
			rt_free(device_partition[index].mte);
			device_partition[index].dev = NULL;	
			device_partition[index].mte = NULL;
			return DFS_STATUS_OK;
		}
	}
	return -DFS_STATUS_ENOENT;
}

static int dfs_jffs2_mkfs(rt_device_t dev_id)
{
	/* just erase all blocks on this nand partition */
	return -DFS_STATUS_ENOSYS; 
}

static int dfs_jffs2_statfs(struct dfs_filesystem* fs, 
                     struct statfs *buf)
{
	/* since the limit of unsigned long, so the max size of flash device is 4G */
	struct cyg_mtab_entry * mte;
	struct jffs2_fs_info info;
	int result;
	
	result = _find_fs(&mte, fs->dev_id);
	if (result)
		return -DFS_STATUS_ENOENT;
		
	RT_ASSERT(mte->data != NULL);

	jffs2_get_info_from_sb((void *)mte->data, &info);
	buf->f_bsize = info.sector_size; 
	buf->f_blocks = info.nr_blocks;
	buf->f_bfree = info.free_size / info.sector_size;
	
	return 0;
}

static const char jffs2_root_path[] = ".";

static int dfs_jffs2_open(struct dfs_fd* file)
{
	int oflag, mode;
	int result;
	cyg_file * jffs2_file;		
	struct cyg_mtab_entry * mte;
	const char * name;

	oflag = file->flags;

	jffs2_file = rt_malloc(sizeof(cyg_file));
	if (jffs2_file == RT_NULL)
		return -DFS_STATUS_ENOMEM;

	/* just escape '/' provided by dfs code */
	name = file->path;
	if ((name[0] == '/') && (name[1] == 0))
		name = jffs2_root_path;
	else /* name[0] still will be '/' */
		name ++;
		
	result = _find_fs(&mte, file->fs->dev_id);		
	if (result) 
	{
		rt_free(jffs2_file);
		return -DFS_STATUS_ENOENT;
	}
 	
	if (oflag & DFS_O_DIRECTORY) /* operations about dir */
	{	
		rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
		if (oflag & DFS_O_CREAT) /* create a dir*/
		{	
			/* fixme, should test file->path can end with '/' */
			result = jffs2_mkdir(mte, mte->root, name);
			if (result)
			{
				rt_mutex_release(&jffs2_lock);
				rt_free(jffs2_file);
				return jffs2_result_to_dfs(result);
			}
		}	
		/* open dir */
		result = jffs2_opendir(mte, mte->root, name, jffs2_file);
		rt_mutex_release(&jffs2_lock);
		if (result)
		{
			rt_free(jffs2_file);
			return jffs2_result_to_dfs(result);			
		}
#ifdef  CONFIG_JFFS2_NO_RELATIVEDIR
		jffs2_file->f_offset = 2;
#endif
		/* save this pointer, it will be used by dfs_jffs2_getdents*/
		file->data = jffs2_file;
		return 0;
	} 
	/* regular file operations */
	mode = 0;
	if (oflag & DFS_O_RDONLY) mode |= JFFS2_O_RDONLY;
	if (oflag & DFS_O_WRONLY) mode |= JFFS2_O_WRONLY;
	if (oflag & DFS_O_RDWR)   mode |= JFFS2_O_RDWR;
	/* Opens the file, if it is existing. If not, a new file is created. */
	if (oflag & DFS_O_CREAT) mode |= JFFS2_O_CREAT;
	/* Creates a new file. If the file is existing, it is truncated and overwritten. */
	if (oflag & DFS_O_TRUNC) mode |= JFFS2_O_TRUNC;
	/* Creates a new file. The function fails if the file is already existing. */
	if (oflag & DFS_O_EXCL) mode |= JFFS2_O_EXCL;
//	if (oflag & DFS_O_APPEND) mode |= JFFS2_O_APPEND;
	rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
    result = jffs2_open(mte, 0, name, mode, jffs2_file);
    if (result != 0)
    {
    	rt_mutex_release(&jffs2_lock);
		rt_free(jffs2_file);
		return jffs2_result_to_dfs(result);
    }
	
	/* save this pointer, it will be used when calling read()��write(), 
	flush(), lessk(), and will be rt_free when calling close()*/
	file->data = jffs2_file;
	file->pos = jffs2_file->f_offset; 
	file->size = 0;
	jffs2_file_lseek(jffs2_file, (off_t *)(&(file->size)), DFS_SEEK_END);
	jffs2_file->f_offset = (off_t)file->pos;
	rt_mutex_release(&jffs2_lock);
	
	if (oflag & DFS_O_APPEND)
	{
		file->pos = file->size;	
 		jffs2_file->f_offset = file->size;
	}	
	return 0; 
}				   

static int dfs_jffs2_close(struct dfs_fd* file)
{
	int result;
	cyg_file * jffs2_file;
	
	RT_ASSERT(file->data != NULL);
	jffs2_file = (cyg_file *)(file->data);
	
	if (file->flags & DFS_O_DIRECTORY) /* operations about dir */
	{
		rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
		result = jffs2_dir_colse(jffs2_file);
		rt_mutex_release(&jffs2_lock);
		if (result)
			return jffs2_result_to_dfs(result);	

		rt_free(jffs2_file);
		return 0;
	}
	/* regular file operations */
	rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
	result = jffs2_file_colse(jffs2_file);
	rt_mutex_release(&jffs2_lock);
	if (result)
		return jffs2_result_to_dfs(result);	
	
	/* release memory */
	rt_free(jffs2_file);
	return 0;
}

static int dfs_jffs2_ioctl(struct dfs_fd* file, int cmd, void* args)
{
	return -DFS_STATUS_ENOSYS;
}

static int dfs_jffs2_read(struct dfs_fd* file, void* buf, rt_size_t len)
{
	cyg_file * jffs2_file;
	struct CYG_UIO_TAG uio_s;
	struct CYG_IOVEC_TAG iovec;
	int char_read;
	int result;
	
	RT_ASSERT(file->data != NULL);
	jffs2_file = (cyg_file *)(file->data);	
    uio_s.uio_iov = &iovec;
    uio_s.uio_iov->iov_base = buf;
    uio_s.uio_iov->iov_len = len;
    uio_s.uio_iovcnt = 1; //must be 1
    //uio_s.uio_offset //not used...
    uio_s.uio_resid = uio_s.uio_iov->iov_len; //seem no use in jffs2;
	
	char_read = jffs2_file->f_offset; /* the current offset */	
	rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
	result = jffs2_file_read(jffs2_file, &uio_s);	
	rt_mutex_release(&jffs2_lock);
	if (result)
		return jffs2_result_to_dfs(result);	
	
	/* update position */
	file->pos = jffs2_file->f_offset;
	char_read = jffs2_file->f_offset - char_read;
	return char_read;
}

static int dfs_jffs2_write(struct dfs_fd* file, 
                    const void* buf, 
					rt_size_t len)
{
	cyg_file * jffs2_file;
	struct CYG_UIO_TAG uio_s;
	struct CYG_IOVEC_TAG iovec;
	int char_write;
	int result;
	
	RT_ASSERT(file->data != NULL);
	jffs2_file = (cyg_file *)(file->data);	
    uio_s.uio_iov = &iovec;
    uio_s.uio_iov->iov_base = (void *)buf;
    uio_s.uio_iov->iov_len = len;
    uio_s.uio_iovcnt = 1; //must be 1
    //uio_s.uio_offset //not used...
    uio_s.uio_resid = uio_s.uio_iov->iov_len; //seem no use in jffs2;
	
	char_write = jffs2_file->f_offset;
	rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
	result = jffs2_file_write(jffs2_file, &uio_s);
	rt_mutex_release(&jffs2_lock);
	if (result)
		return jffs2_result_to_dfs(result);
	
	/* update position */
	file->pos = jffs2_file->f_offset;	
	char_write = jffs2_file->f_offset - char_write;
	return char_write;
}

static int dfs_jffs2_flush(struct dfs_fd* file)
{
	/* infact, jffs2 not support, jffs2_fo_sync just return ok */
	return -DFS_STATUS_ENOSYS;
}

/* fixme warning: the offset is rt_off_t, so maybe the size of a file is must <= 2G*/
static int dfs_jffs2_lseek(struct dfs_fd* file, 
                    rt_off_t offset)
{
	cyg_file * jffs2_file;
	int result;
	
	RT_ASSERT(file->data != NULL);
	jffs2_file = (cyg_file *)(file->data);		
	
	/* set offset as current offset */
	rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
	result = jffs2_file_lseek(jffs2_file, &offset, DFS_SEEK_SET);
	rt_mutex_release(&jffs2_lock);
	if (result)
		return jffs2_result_to_dfs(result);
	/* update file position */
	file->pos = offset;
	return offset;
}

/* return the size of  struct dirent*/
static int dfs_jffs2_getdents(struct dfs_fd* file, 
                       struct dirent* dirp, 
					   rt_uint32_t count)
{
	cyg_file * jffs2_file;
	struct CYG_UIO_TAG uio_s;
	struct CYG_IOVEC_TAG iovec;
	struct jffs2_dirent jffs2_d;
	struct dirent * d;
	rt_uint32_t index;
#if !defined (CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE)
	struct jffs2_stat s;
	cyg_mtab_entry * mte;
	char * fullname;
#endif
	int result;
	
	RT_ASSERT(file->data != RT_NULL);	
	jffs2_file = (cyg_file*)(file->data);
	
	//set jffs2_d
	memset(&jffs2_d, 0, sizeof(struct jffs2_dirent));
	//set CYG_UIO_TAG uio_s
	uio_s.uio_iov = &iovec;
	uio_s.uio_iov->iov_base = &jffs2_d;
	uio_s.uio_iov->iov_len = sizeof(struct jffs2_dirent);;
	uio_s.uio_iovcnt = 1; //must be 1
	uio_s.uio_offset = 0;//not used...
	uio_s.uio_resid = uio_s.uio_iov->iov_len; //seem no use in jffs2;	

#if !defined (CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE)

	result = _find_fs(&mte, file->fs->dev_id);
	if (result)
		return -DFS_STATUS_ENOENT;
#endif

	/* make integer count, usually count is 1 */
	count = (count / sizeof(struct dirent)) * sizeof(struct dirent);
	if (count == 0) 
		return -DFS_STATUS_EINVAL;

	index = 0;
	/* usually, the while loop should only be looped only once! */
	while (1)
	{
		d = dirp + index;
		rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
		result = jffs2_dir_read(jffs2_file, &uio_s); 
		rt_mutex_release(&jffs2_lock);
		/* if met a error or all entry are read over, break while*/
		if (result || jffs2_d.d_name[0] == 0)
			break;

#if defined (CYGPKG_FS_JFFS2_RET_DIRENT_DTYPE)
		switch(jffs2_d.d_type & JFFS2_S_IFMT) 
		{ 
		case JFFS2_S_IFREG: d->d_type = DFS_DT_REG; break; 		
		case JFFS2_S_IFDIR: d->d_type = DFS_DT_DIR; break; 
		default: d->d_type = DFS_DT_UNKNOWN; break; 
		} 	
#else
		fullname = rt_malloc(FILE_PATH_MAX);
		if(fullname == RT_NULL)
				return -DFS_STATUS_ENOMEM;

		/* make a right entry */
		if ((file->path[0] == '/') )
		{
			if (file->path[1] == 0)
				strcpy(fullname, jffs2_d.d_name);
			else
				rt_sprintf(fullname, "%s/%s", file->path+1, jffs2_d.d_name);
		}
		else
			rt_sprintf(fullname, "%s/%s", file->path, jffs2_d.d_name);
		rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
		result = jffs2_porting_stat(mte, mte->root, fullname, (void *)&s);
		rt_mutex_release(&jffs2_lock);
		if (result)
			return jffs2_result_to_dfs(result);

		rt_free(fullname);
		/* convert to dfs stat structure */
		switch(s.st_mode & JFFS2_S_IFMT)
		{
		case JFFS2_S_IFREG: d->d_type = DFS_DT_REG; break;
		case JFFS2_S_IFDIR: d->d_type = DFS_DT_DIR; break;
		default: d->d_type = DFS_DT_UNKNOWN; break;
		}
#endif
		/* write the rest fields of struct dirent* dirp  */
		d->d_namlen = rt_strlen(jffs2_d.d_name);
		d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
		rt_strncpy(d->d_name, jffs2_d.d_name, d->d_namlen + 1);
		
		index ++;
		if (index * sizeof(struct dirent) >= count)
			break;
	}
	if (result)
		return jffs2_result_to_dfs(result);
	return index * sizeof(struct dirent);
}

static int dfs_jffs2_unlink(struct dfs_filesystem* fs, const char* path)
{
	int result;
	struct jffs2_stat s;
	cyg_mtab_entry * mte;
			
	result = _find_fs(&mte, fs->dev_id);		
	if (result) 
		return -DFS_STATUS_ENOENT;
		
	/* deal path */
	if (path[0] == '/')
		path++;
	
	/* judge file type, dir is to be delete by rmdir, others by unlink */
	rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
	result = jffs2_porting_stat(mte, mte->root, path, (void *)&s);
	if (result)
	{
		rt_mutex_release(&jffs2_lock);
		return jffs2_result_to_dfs(result);
	}

	switch(s.st_mode & JFFS2_S_IFMT)
	{
	case JFFS2_S_IFREG:
		result = jffs2_file_unlink(mte, mte->root, path);
		break;
	case JFFS2_S_IFDIR:
		result = jffs2_rmdir(mte, mte->root, path);
		break;
	default:
		/* unknown file type */
		rt_mutex_release(&jffs2_lock);
		return -1;
	}
	rt_mutex_release(&jffs2_lock);
	if (result) 
		return jffs2_result_to_dfs(result);
	return 0;			
}

static int dfs_jffs2_rename(struct dfs_filesystem* fs, 
                     const char* oldpath,
					 const char* newpath)
{
	int result;
	cyg_mtab_entry * mte;
			
	result = _find_fs(&mte, fs->dev_id);		
	if (result) 
		return -DFS_STATUS_ENOENT;
			
	if (*oldpath == '/')
		oldpath += 1;
	if (*newpath == '/')
		newpath += 1;
	rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
	result = jffs2_rename(mte, mte->root, oldpath, mte->root, newpath);
	rt_mutex_release(&jffs2_lock);
	if (result)
		return jffs2_result_to_dfs(result);
	return 0;
}

static int dfs_jffs2_stat(struct dfs_filesystem* fs, const char *path, struct stat *st)
{
	int result;
	struct jffs2_stat s;
	cyg_mtab_entry * mte;
		
	/* deal the path for jffs2 */
	RT_ASSERT(!((path[0] == '/') && (path[1] == 0)));
	
	if (path[0] == '/')
		path++;

	result = _find_fs(&mte, fs->dev_id);		
	if (result) 
		return -DFS_STATUS_ENOENT;

	rt_mutex_take(&jffs2_lock, RT_WAITING_FOREVER);
	result = jffs2_porting_stat(mte, mte->root, path, (void *)&s);
	rt_mutex_release(&jffs2_lock);

	if (result)
		return jffs2_result_to_dfs(result);
	/* convert to dfs stat structure */
	switch(s.st_mode & JFFS2_S_IFMT) 
	{ 
	case JFFS2_S_IFREG: 
		st->st_mode = DFS_S_IFREG | DFS_S_IRUSR | DFS_S_IRGRP | DFS_S_IROTH |
		DFS_S_IWUSR | DFS_S_IWGRP | DFS_S_IWOTH;
		break;

	case JFFS2_S_IFDIR:
		st->st_mode = DFS_S_IFDIR | DFS_S_IXUSR | DFS_S_IXGRP | DFS_S_IXOTH;
		break;		

	default: 
		st->st_mode = DFS_DT_UNKNOWN; //fixme
		break; 
	} 	

	st->st_dev  = 0;
	st->st_size = s.st_size;
	st->st_mtime = s.st_mtime;

	return 0;
}

static const struct dfs_filesystem_operation dfs_jffs2_ops = 
{
	"jffs2", /* file system type: jffs2 */
#if RTTHREAD_VERSION >= 10100
	DFS_FS_FLAG_DEFAULT, /* use Relative Path */
#endif
	dfs_jffs2_mount,
	dfs_jffs2_unmount,
	dfs_jffs2_mkfs,
	dfs_jffs2_statfs,

	dfs_jffs2_open,
	dfs_jffs2_close,
	dfs_jffs2_ioctl,
	dfs_jffs2_read,
	dfs_jffs2_write,
	dfs_jffs2_flush,
	dfs_jffs2_lseek,
	dfs_jffs2_getdents,
	dfs_jffs2_unlink,
	dfs_jffs2_stat,
	dfs_jffs2_rename,
};

int dfs_jffs2_init(void)
{
    /* register fatfs file system */
    dfs_register(&dfs_jffs2_ops);
    /* initialize mutex */
	if (rt_mutex_init(&jffs2_lock, "jffs2lock", RT_IPC_FLAG_FIFO) != RT_EOK)
	{
		rt_kprintf("init jffs2 lock mutex failed\n");
	}
	rt_kprintf("init jffs2 lock mutex okay\n");
	return 0;
}
INIT_FS_EXPORT(dfs_jffs2_init);

